package pl.edu.agh.gronoMining.owlToGraphConverter.parser.implementation;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import pl.edu.agh.gronoMining.owlToGraphConverter.graph.Graph;
import pl.edu.agh.gronoMining.owlToGraphConverter.logger.Logger;
import pl.edu.agh.gronoMining.owlToGraphConverter.parser.IOwlParser;
import pl.edu.agh.gronoMining.owlToGraphConverter.parser.utils.IConverter;
import pl.edu.agh.gronoMining.owlToGraphConverter.parser.utils.IDataManager;
import pl.edu.agh.gronoMining.owlToGraphConverter.parser.utils.NodeData;
import pl.edu.agh.gronoMining.owlToGraphConverter.parser.utils.implementation.NodesManager;
import edu.stanford.smi.protegex.owl.ProtegeOWL;
import edu.stanford.smi.protegex.owl.model.OWLIndividual;
import edu.stanford.smi.protegex.owl.model.OWLModel;
import edu.stanford.smi.protegex.owl.model.OWLNamedClass;
import edu.stanford.smi.protegex.owl.model.OWLObjectProperty;
import edu.stanford.smi.protegex.owl.model.OWLProperty;

public class OwlParser2 implements IOwlParser {

	private IDataManager dataManager;
	private IConverter converter;
	
	private NodesManager<String> usersManager;
	private NodesManager<String> gronoNameManager;
	private NodesManager<String> gronoSubjectManager;
	private NodesManager<String> cityManager;
	private NodesManager<String> provinceManager;
	private NodesManager<String> countryManager;
	
	private static Logger logger = Logger.getLogger(OwlParser2.class);
	
	public OwlParser2 (IDataManager dataManager, IConverter converter) {
		
		this.dataManager = dataManager;
		this.converter = converter;
		
		this.usersManager = new NodesManager<String>();
		this.gronoNameManager = new NodesManager<String>();
		this.gronoSubjectManager = new NodesManager<String>();
		this.cityManager = new NodesManager<String>();
		this.provinceManager = new NodesManager<String>();
		this.countryManager = new NodesManager<String>();
		
		logger.info("Parser inicialized");
	}
	
	public void prepare() {
		
	}
	
	public Graph[] parse(InputStream in) throws IOException, ParseException {
		
		logger.info("Parser started");
		Graph graph = new Graph();
		List<Graph> graphsList = new LinkedList<Graph>();
		try {
			
			BufferedReader bin = new BufferedReader(new InputStreamReader(in));
			OWLModel owlModel = ProtegeOWL.createJenaOWLModelFromReader(bin);
			
			logger.info("model created");
			
			OWLNamedClass classUser = owlModel.getOWLNamedClass("User");
			OWLNamedClass classGrono = owlModel.getOWLNamedClass("Grono");
			OWLNamedClass classCity = owlModel.getOWLNamedClass("Miasto");
			OWLNamedClass classProvince = owlModel.getOWLNamedClass("Wojewodztwo");
			OWLNamedClass classCountry = owlModel.getOWLNamedClass("Kraj");
			
			OWLProperty propertyMaPlec = owlModel.getOWLProperty("maPlec");
			OWLProperty propertyZarejestrowalSie = owlModel.getOWLProperty("zarejestrowalSie");
			OWLProperty propertyMaImie = owlModel.getOWLProperty("maImie");
			OWLProperty propertyOstatnioLogowalSie = owlModel.getOWLProperty("ostatnioLogowalSie");
			OWLProperty propertySzuka = owlModel.getOWLProperty("szuka");
			OWLProperty propertyMaNazwe = owlModel.getOWLProperty("maNazwe");
			OWLProperty propertyZawieraSieWKategorii = owlModel.getOWLProperty("zawieraSieWKategorii");
			OWLProperty propertyUrodzilSie = owlModel.getOWLProperty("urodzilSie");
			
			OWLProperty propertyPiszeNaNim = owlModel.getOWLProperty("piszeNaNim");
			OWLObjectProperty objPropertyZawieraSieW = owlModel.getOWLObjectProperty("zawieraSieW");
			OWLObjectProperty objPropertyGraniczyZ = owlModel.getOWLObjectProperty("graniczyZ");
			OWLProperty propertyPiszeNa = owlModel.getOWLProperty("piszeNa");
			OWLProperty propertyPochodziZNiego = owlModel.getOWLProperty("pochodziZNiego");
			OWLProperty propertyMaZnajomego = owlModel.getOWLProperty("maZnajomego");
			OWLProperty propertyPochodziZ = owlModel.getOWLProperty("pochodziZ");
			OWLObjectProperty objPropertyZawiera = owlModel.getOWLObjectProperty("zawiera");
			OWLProperty propertyZaproszonyPrzez = owlModel.getOWLProperty("zaproszonyPrzez");
			
			logger.info("classes and properties created");
			
						
			Collection instancesOfUser = classUser.getInstances(false);
			for (Object userObj : instancesOfUser) {
				OWLIndividual userIndividual = (OWLIndividual) userObj;

				//Graph graph = new Graph();
				
				String id = userIndividual.getName();
				logger.info(" === parsing user "+id);
				
				Integer userNodeId = usersManager.getNode(id);
				if (userNodeId == null) {
					userNodeId = graph.addNode(NodeData.USER, dataManager.getInt(new NodeData(NodeData.USER, null)), id);
					usersManager.setNode(id, userNodeId);
					logger.info("add node - [" + userNodeId + "] "+id);
				} else {
					logger.info("user node exists ("+userNodeId+")");
				}
				
				if (userIndividual.hasPropertyValue(propertyMaImie)) {
					String name = (String) userIndividual.getPropertyValue(propertyMaImie);
					name = name.trim();
					if (! "".equals(name)) {
						NodeData nameNodeData = new NodeData(NodeData.IMIE, name);
						int nameNodeId = graph.addNode(NodeData.IMIE, dataManager.getInt(nameNodeData), name);
						logger.info(userNodeId);
						logger.info(nameNodeId);
						graph.addEdge(userNodeId, nameNodeId);
						logger.info("name added ["+nameNodeId+"] - "+name);
					}
				}
				
				if (userIndividual.hasPropertyValue(propertyMaPlec)) {
					try {
						String sex = (String) userIndividual.getPropertyValue(propertyMaPlec);
						int sexInt = converter.sex(sex);
						NodeData sexNodeData = new NodeData(NodeData.PLEC, sexInt);
						int sexNodeId = graph.addNode(NodeData.PLEC,dataManager.getInt(sexNodeData), "plec-"+sexInt);
						graph.addEdge(userNodeId, sexNodeId);
						logger.info("sex added ["+sexNodeId+"] - "+sexInt);
					} catch (ParseException e) {
						
					}
				}
				
				if (userIndividual.hasPropertyValue(propertyZarejestrowalSie)) {
					try {
						String registrationDate = (String) userIndividual.getPropertyValue(propertyZarejestrowalSie);
						int registrationInterval = converter.registryDate(registrationDate);
						NodeData registrationNodeData = new NodeData(NodeData.DATA_REJESTRACJI, registrationInterval);
						int registrationNodeId = graph.addNode(NodeData.DATA_REJESTRACJI,dataManager.getInt(registrationNodeData),"rejestracja-"+registrationInterval);
						graph.addEdge(userNodeId, registrationNodeId);
						logger.info("registration date added ["+registrationNodeId+"] - "+registrationInterval);
					} catch (ParseException e) {
						
					}
				}
				
				if (userIndividual.hasPropertyValue(propertyUrodzilSie)) {
					try {
						String birthDate = (String) userIndividual.getPropertyValue(propertyUrodzilSie);
						int ageInterval = converter.age(birthDate);
						NodeData ageNodeData = new NodeData(NodeData.WIEK, ageInterval);
						int ageNodeId = graph.addNode(NodeData.WIEK,dataManager.getInt(ageNodeData),"wiek-"+ageInterval);
						graph.addEdge(userNodeId, ageNodeId);
						logger.info("age added ["+ageNodeId+"] - " + ageInterval);
					} catch (ParseException e) {
						
					}
				}
				
				if (userIndividual.hasPropertyValue(propertyOstatnioLogowalSie)) {
					try {
						String lastLogDate = (String) userIndividual.getPropertyValue(propertyOstatnioLogowalSie);
						int lastLogDateInterval = converter.lastLogTime(lastLogDate);
						NodeData lastLogNodeData = new NodeData(NodeData.CZAS_LOGOWANIA, lastLogDateInterval);
						int lastLogNodeId = graph.addNode(NodeData.CZAS_LOGOWANIA,dataManager.getInt(lastLogNodeData),"ostatniLog-"+lastLogDateInterval);
						graph.addEdge(userNodeId, lastLogNodeId);
						logger.info("last log added ["+lastLogNodeId+"] - "+lastLogDateInterval);
					} catch (ParseException e) {
						
					}
				}
				
				if (userIndividual.hasPropertyValue(propertySzuka)) {
					Collection lookForObjects = userIndividual.getPropertyValues(propertySzuka);
					for (Object lookForObj : lookForObjects) {
						String lookForName = (String) lookForObj;
						try {
							int interval = converter.lookFor(lookForName);
							NodeData lookForNodeData = new NodeData(NodeData.SZUKA, interval);
							int lookForNodeId = graph.addNode(NodeData.SZUKA,dataManager.getInt(lookForNodeData),"szuka-"+interval);
							graph.addEdge(userNodeId, lookForNodeId);
							logger.info("look for added ["+lookForNodeId+"] - " + interval);
						} catch (ParseException e) {
							
						}
					}
				}
				
				if (userIndividual.hasPropertyValue(propertyMaZnajomego)) {
					Collection friendsCollection = userIndividual.getPropertyValues(propertyMaZnajomego);
					for (Object friendObj : friendsCollection) {
						String friendId = (String) friendObj;
						friendId = friendId.trim();
						logger.info("ma znajomego "+friendId);
						Integer friendNodeId = usersManager.getNode(friendId);
						if (friendNodeId == null) {
							//logger.info(graph);
							//logger.info(dataManager);
							//logger.info(NodeData.USER);
							//logger.info(NodeData.USER);
							friendNodeId = graph.addNode(NodeData.USER, dataManager.getInt(new NodeData(NodeData.USER, null)));
							usersManager.setNode(friendId, friendNodeId);
							logger.info("add node - [" + friendNodeId + "] - "+friendId);
							continue;
						}else {
							if (! graph.hasEdge(userNodeId, friendNodeId)) {
								graph.addEdge(userNodeId, friendNodeId);
								logger.info(" - add edge ["+userNodeId+"]" + id + " - ["+friendNodeId+"]" + friendId);
							}
						}
						logger.info("znajomy node id = "+friendNodeId);
						
					}
				}
				
				if (userIndividual.hasPropertyValue(propertyZaproszonyPrzez)) {
					String invitedById = (String) userIndividual.getPropertyValue(propertyZaproszonyPrzez);
					Integer invitedByNodeId = usersManager.getNode(invitedById);
					if (invitedByNodeId == null) {
						invitedByNodeId = graph.addNode(NodeData.USER, dataManager.getInt(new NodeData(NodeData.USER, null)));
						usersManager.setNode(invitedById, invitedByNodeId);
						logger.info("add node - [" + invitedByNodeId + "] - "+invitedById);
					}
					int tempNodeId = graph.addNode(NodeData.ZAPROSZONY_PRZEZ, dataManager.getInt(new NodeData(NodeData.ZAPROSZONY_PRZEZ, null)));
					graph.addEdge(userNodeId, tempNodeId);
					graph.addEdge(invitedByNodeId, tempNodeId);
					//graph.addEdge(countryNodeId, nodeId);
					logger.info(" - add edge - ["+userNodeId+"]"+id + " - [" + invitedByNodeId+"]"+invitedById);
				}
				
				if (userIndividual.hasPropertyValue(propertyPiszeNa)) {
					Collection gronoCollection = userIndividual.getPropertyValues(propertyPiszeNa);
					for (Object gronoObj : gronoCollection) {
						String gronoName = (String) gronoObj;
						gronoName = gronoName.trim();
						Integer gronoNodeId = gronoNameManager.getNode(gronoName);
						if (gronoNodeId == null) {
							gronoNodeId = graph.addNode(NodeData.NAZWA_GRONA, dataManager.getInt(new NodeData(NodeData.NAZWA_GRONA, gronoName)));
							gronoNameManager.setNode(gronoName, gronoNodeId);
							logger.info("grono added ["+gronoNodeId+"] - " + gronoName);
						}
						if (! graph.hasEdge(userNodeId, gronoNodeId)) {
							graph.addEdge(userNodeId, gronoNodeId);
							logger.info(" - add edge ["+userNodeId+"]" + id + " - ["+gronoNodeId+"]" + gronoName);
						}						
					}
				}
				
				if (userIndividual.hasPropertyValue(propertyPochodziZ)) {
					String fromName = (String) userIndividual.getPropertyValue(propertyPochodziZ);
					fromName = fromName.trim();
					if (! "".equals(fromName)) {
						Integer fromNodeId = cityManager.getNode(fromName);
						if (fromNodeId == null) {
							NodeData fromNodeData = new NodeData(NodeData.MIASTO, fromName);
							fromNodeId = graph.addNode(NodeData.MIASTO, dataManager.getInt(fromNodeData));
							cityManager.setNode(fromName, fromNodeId);
							logger.info("city added ["+fromNodeId+"] - " + fromName);
						}
						graph.addEdge(userNodeId, fromNodeId);
						logger.info(" - add edge ["+userNodeId+"]" + id + " - ["+fromNodeId+"]" + fromName);
					}	
				}
				
				//graphsList.add(graph);
				
			}
			
			logger.info("users parsed");
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		graphsList.add(graph);
		
		Graph[] result = new Graph[graphsList.size()];
		int i = 0;
		for (Graph g : graphsList) {
			result[i] = g;
			i++;
		}
		
		logger.info("Parser ended");
		return result;
	}
	
}
